package com.mofeng.myshop.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mofeng.myshop.domain.dto.AddressMapDto;
import com.mofeng.myshop.domain.dto.search.AddressMapSearchDto;
import com.mofeng.myshop.domain.po.Address;
import com.mofeng.myshop.domain.po.AddressMap;
import com.mofeng.myshop.domain.po.Project;
import com.mofeng.myshop.domain.po.base.BaseEntity;
import com.mofeng.myshop.domain.vo.AddressMapVo;
import com.mofeng.myshop.domain.vo.CommodityVo;
import com.mofeng.myshop.domain.vo.ProjectVo;
import com.mofeng.myshop.domain.vo.base.PageMp;
import com.mofeng.myshop.mapper.AddressMapMapper;
import com.mofeng.myshop.mapper.AddressMapper;
import com.mofeng.myshop.mapper.ProjectMapper;
import com.mofeng.myshop.service.IAddressService;
import com.mofeng.myshop.service.PermissionService;
import com.mofeng.myshop.until.MyUtil;
import com.mofeng.myshop.until.R;
import com.mofeng.myshop.until.component.RedisUtil;
import com.mofeng.myshop.until.rcon.Rcon;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.solr.client.solrj.response.Group;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 发货地址 服务实现类
 * </p>
 *
 * @author MoFeng
 * @since 2024-04-16
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AddressServiceImpl implements IAddressService {

    private final AddressMapMapper addressMapMapper;

    private final ProjectMapper projectMapper;

    private final PermissionService permissionService;

    private final RedisUtil redisUtil;

    /**
     * 保存地图信息
     *
     * @param addressMapDto 地图信息
     */
    @Override
    public R saveAddress(AddressMapDto addressMapDto) {
        AddressMap addressMap = addressMapDto.toAddressMap();
        Long id = addressMap.getId();
        AddressMap addressMapResult = null;
        String msg = "";
        if (id == null) {
            addressMapResult = save(addressMap);
            msg = "地图创建成功";
        } else {
            addressMapResult = update(addressMap);
            msg = "地图修改成功";
        }
        AddressMapVo addressMapVo = new AddressMapVo(addressMapResult);
        return R.SUCCESS(msg, addressMapVo);
    }


    private AddressMap save(AddressMap addressMap) {
        String password = addressMap.getPassword();
        if (MyUtil.StringUtil.isEmpty(password)) {
            throw new RuntimeException("密码不能为空");
        }
        String ip = addressMap.getIp();
        if (MyUtil.StringUtil.isEmpty(ip)) {
            throw new RuntimeException("IP不能为空");
        }
        int rconPort = addressMap.getRconPort();
        if (MyUtil.StringUtil.isEmpty(rconPort)) {
            throw new RuntimeException("Rcon端口不能为空");
        }
        int serverPort = addressMap.getServerPort();
        if (MyUtil.StringUtil.isEmpty(serverPort)) {
            throw new RuntimeException("服务器端口不能为空");
        }
        addressMapMapper.insert(addressMap);
        return addressMap;
    }

    private AddressMap update(AddressMap addressMap) {

        addressMapMapper.updateById(addressMap);
        return addressMap;
    }

    /**
     * 删除地图信息
     *
     * @param ids 地图信息
     */
    @Override
    public R delAddress(List<String> ids) {
        List<Long> idList = ids.stream().map(Long::parseLong).collect(Collectors.toList());
        addressMapMapper.deleteBatchIds(idList);
        return R.SUCCESS("地图删除成功");
    }

    /**
     * 获取地图列表
     *
     * @param addressMapSearchDto 地图搜索类
     */
    @Override
    public R getAddressList(AddressMapSearchDto addressMapSearchDto) {
        String mapType = addressMapSearchDto.getMapType();
        Long projectId = addressMapSearchDto.getProjectId();
        Integer port = addressMapSearchDto.getPort();
        Page<AddressMap> pageMp = addressMapSearchDto.getPageMp();
        Page<AddressMap> result = addressMapMapper.findByList(projectId, mapType, port, pageMp);
        List<Project> projectList = projectMapper.findAll();
        Map<Long, List<Project>> projectMap = projectList.stream().collect(Collectors.groupingBy(BaseEntity::getId));
        List<AddressMap> records = result.getRecords();
        boolean root = permissionService.ROOT();
        for (AddressMap map : records) {
            Long projectId1 = map.getProjectId();
            if (projectId1 != null) {
                List<Project> projectList1 = projectMap.get(projectId1);
                map.setProject(projectList1.get(0));
            }
            if (!root) {
                map.setPassword(null);
                map.setIp(null);
                map.setRconPort(-1);
                map.setServerPort(-1);
            }
        }
        return R.SUCCESS("地图获取成功", new PageMp<>(result, AddressMapVo::new));
    }

    private final String mapPrefix = "map_";
    private final Long saveTime = 600L;

    /**
     * 获取地图 服务器 --> 服务器状态
     */
    @Override
    public R getServerMsg() {
        List<Project> projectList = projectMapper.findAll();
        List<AddressMap> addressMapList = addressMapMapper.findAll();
        addressMapList = addressMapList.stream().sorted(Comparator.comparing(BaseEntity::getOrderIndex)).toList();
        List<AddressMapVo> addressMapVoList = new ArrayList<>();
        for (AddressMap addressMap : addressMapList) {
            Long id = addressMap.getId();
            String mapRedis = mapPrefix + id;
            Boolean mapStateObj = (Boolean) redisUtil.get(mapRedis);

            String host = addressMap.getIp();
            int rconPort = addressMap.getRconPort();
            String password = addressMap.getPassword();
            String ipAddress = "";
            try {
                ipAddress = MyUtil.ServletUtil.getIPAddress(host);
            } catch (Exception e) {
                continue;
            }

            if (mapStateObj == null) {
                mapStateObj = checkIsOnline(ipAddress, rconPort, password);
                redisUtil.set(mapRedis, mapStateObj, saveTime);
            }
            int serverPort = addressMap.getServerPort();
            AddressMapVo addressMapVo = new AddressMapVo(addressMap);
            addressMapVo.setPassword(null);
            addressMapVo.setIp(null);
            addressMapVo.setRconPort(-1);
            addressMapVo.setServerPort(-1);
            addressMapVo.setOpenLink(ipAddress + ":" + serverPort);
            addressMapVo.setOnLine(mapStateObj);
            addressMapVoList.add(addressMapVo);
        }
        List<ProjectVo> projectVoList = ProjectVo.change(projectList);
        Map<String, List<AddressMapVo>> addressMap = addressMapVoList.stream().collect(Collectors.groupingBy(AddressMapVo::getProjectId));
        for (ProjectVo projectVo : projectVoList) {
            String id = projectVo.getId();
            List<AddressMapVo> addressMapVos = addressMap.get(id);
            if (addressMapVos != null) {
                String link = projectVo.getLink();
                if (link != null) {
                    addressMapVos.forEach(addressMapVo -> {
                        String openLink = addressMapVo.getOpenLink();
                        addressMapVo.setOpenLink(link + openLink);
                    });
                } else {
                    addressMapVos.forEach(addressMapVo -> {
                        addressMapVo.setOpenLink(null);
                    });
                }

                projectVo.setAddressList(addressMapVos);
            }
        }
        return R.SUCCESS().setData(projectVoList);
    }

    private final String payload = "GetServerFrames";

    /**
     * 检查服务器是否在线
     *
     * @param addressMap 地图信息
     */
    private boolean checkIsOnline(AddressMap addressMap) {
        boolean onLine = false;
        String host = addressMap.getIp();
        int rconPort = addressMap.getRconPort();
        String password = addressMap.getPassword();
        try {
            String ipAddress = MyUtil.ServletUtil.getIPAddress(host);
            onLine = checkIsOnline(ipAddress, rconPort, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return onLine;
    }

    /**
     * 检查服务器是否在线
     *
     * @param ip       IP
     * @param port     端口
     * @param password 密码
     */
    private boolean checkIsOnline(String ip, int port, String password) {
        boolean onLine = false;
        try {
            Rcon rcon = new Rcon();
            rcon.setTimeout(1000);
            rcon.connect(ip, port, password.getBytes());
            String result = rcon.command(payload);
            if (result.contains("Serverframes")) {
                onLine = true;
            } else {
                log.error("连接出现问题,服务器返回结果：{}", result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return onLine;
    }

    /**
     * 检查服务器状态
     * cron 属性支持使用 Cron 表达式来配置定时任务。0 0/5 * * * ? 这个表达式的含义是：
     * <p>
     * 0：秒，表示在每分钟的第 0 秒执行。
     * 0/5：分钟，表示每 5 分钟执行一次。
     * *：小时，表示每个小时。
     * *：日期，表示每个月的每一天。
     * *：月份，表示每年中的每个月。
     * ?：星期，表示不指定具体的星期几。
     * 这个表达式的意思是：每个小时的第 0 分钟开始，每五分钟执行一次任务。
     */
    @Scheduled(cron = "0 0/3 * * * ?")
    public void checkAllOnLine() {
        List<AddressMap> addressMapList = addressMapMapper.findAll();
        for (AddressMap addressMap : addressMapList) {
            boolean b = checkIsOnline(addressMap);
            Long id = addressMap.getId();
            String mapRedis = mapPrefix + id;
            redisUtil.set(mapRedis, b, saveTime);
        }
    }
}
